import mongoose from 'mongoose';
import { createClient } from 'redis';
import config from '@config/index';
import logger from './logger';

export interface HealthStatus {
  status: 'healthy' | 'unhealthy' | 'degraded';
  timestamp: string;
  version: string;
  environment: string;
  uptime: number;
  checks: {
    database: CheckResult;
    redis: CheckResult;
    memory: CheckResult;
  };
}

export interface CheckResult {
  status: 'pass' | 'fail' | 'warn';
  message?: string;
  responseTime?: number;
  details?: any;
}

/**
 * Check MongoDB connection health
 */
export async function checkDatabase(): Promise<CheckResult> {
  const startTime = Date.now();

  try {
    if (mongoose.connection.readyState !== 1) {
      return {
        status: 'fail',
        message: 'Database not connected',
        responseTime: Date.now() - startTime,
      };
    }

    // Perform a simple ping operation
    if (mongoose.connection.db) {
      await mongoose.connection.db.admin().ping();
    }

    return {
      status: 'pass',
      message: 'Database connection healthy',
      responseTime: Date.now() - startTime,
      details: {
        readyState: mongoose.connection.readyState,
        host: mongoose.connection.host,
        name: mongoose.connection.name,
      },
    };
  } catch (error) {
    logger.error('Database health check failed:', error as Error);
    return {
      status: 'fail',
      message: `Database check failed: ${(error as Error).message}`,
      responseTime: Date.now() - startTime,
    };
  }
}

/**
 * Check Redis connection health
 */
export async function checkRedis(): Promise<CheckResult> {
  const startTime = Date.now();

  try {
    const redis = createClient({
      url: config.env.REDIS_URI,
    });

    await redis.connect();
    await redis.ping();
    await redis.quit();

    return {
      status: 'pass',
      message: 'Redis connection healthy',
      responseTime: Date.now() - startTime,
    };
  } catch (error) {
    logger.warn('Redis health check failed:', error as Error);
    // Redis is not critical, so we return 'warn' instead of 'fail'
    return {
      status: 'warn',
      message: `Redis unavailable: ${(error as Error).message}`,
      responseTime: Date.now() - startTime,
    };
  }
}

/**
 * Check memory usage
 */
export function checkMemory(): CheckResult {
  const usage = process.memoryUsage();
  const heapUsedMB = Math.round(usage.heapUsed / 1024 / 1024);
  const heapTotalMB = Math.round(usage.heapTotal / 1024 / 1024);
  const heapUsagePercent = Math.round((usage.heapUsed / usage.heapTotal) * 100);

  let status: 'pass' | 'warn' | 'fail' = 'pass';
  let message = 'Memory usage normal';

  // More lenient thresholds for development environment
  if (heapUsagePercent > 95) {
    status = 'fail';
    message = 'Memory usage critical';
  } else if (heapUsagePercent > 85) {
    status = 'warn';
    message = 'Memory usage high';
  }

  return {
    status,
    message,
    details: {
      heapUsed: `${heapUsedMB}MB`,
      heapTotal: `${heapTotalMB}MB`,
      heapUsagePercent: `${heapUsagePercent}%`,
      rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
      external: `${Math.round(usage.external / 1024 / 1024)}MB`,
    },
  };
}

/**
 * Perform comprehensive health check
 */
export async function performHealthCheck(): Promise<HealthStatus> {
  const [dbCheck, redisCheck] = await Promise.all([
    checkDatabase(),
    checkRedis(),
  ]);

  const memoryCheck = checkMemory();

  // Determine overall status
  let overallStatus: 'healthy' | 'unhealthy' | 'degraded' = 'healthy';

  if (dbCheck.status === 'fail' || memoryCheck.status === 'fail') {
    overallStatus = 'unhealthy';
  } else if (
    dbCheck.status === 'warn' ||
    redisCheck.status === 'warn' ||
    memoryCheck.status === 'warn'
  ) {
    overallStatus = 'degraded';
  }

  return {
    status: overallStatus,
    timestamp: new Date().toISOString(),
    version: process.env.npm_package_version || '1.0.0',
    environment: config.env.NODE_ENV,
    uptime: Math.floor(process.uptime()),
    checks: {
      database: dbCheck,
      redis: redisCheck,
      memory: memoryCheck,
    },
  };
}

/**
 * Check if service is ready to accept traffic
 * This is stricter than health check - all critical dependencies must be available
 */
export async function performReadinessCheck(): Promise<{
  ready: boolean;
  message: string;
  checks: any;
}> {
  const dbCheck = await checkDatabase();
  const memoryCheck = checkMemory();

  const ready = dbCheck.status === 'pass' && memoryCheck.status !== 'fail';

  return {
    ready,
    message: ready ? 'Service is ready' : 'Service is not ready',
    checks: {
      database: dbCheck,
      memory: memoryCheck,
    },
  };
}
